//@author A0097972J
package logic;

import gui.IUserInterface;
import hirondelle.date4j.DateTime;

import java.util.HashMap;
import java.util.List;

import common.AttributeType;
import common.Command;
import common.CommandType;
import common.DateHelper;
import common.Task;
import common.TaskStatus;

/**
 * Search Command Handler provides implementations for both Search and Display
 * commands (Display is treat as search with restricted search value) <br/>
 * 
 */

public class SearchCommandHandler extends CommandHandler {
	private final String DISPLAY_MESSAGE = "\n\nList of %s tasks:\n";
	private final String SEARCH_MESSAGE = "\n\nSearch Result:\n";
	private final String NO_RESULT_MESSAGE = "\n\nNo task match your search\n";
	private final String FREE_SLOTS = "\nFree slots available: \n";
	private final String RANGE_BELOW_ZERO = "Input error. The range is below 0\n";
	private final String RANGE_NULL = "Syntax error: You didn't specify the range.\nPress F10 for more help\n";
	private final String STARTING_NULL = "Syntax error: You didn't specify the starting time.\nPress F10 for more help\n";
	private final String ENDING_NULL = "Syntax error: You didn't specify the ending time.\nPress F10 for more help\n";
	private final String INPUT_ERROR = "Input error. Starting date or ending date cannot be converted\n";
	private final String REGEX_SPLITTER = "\\s+";
	private final String DATE_FORMAT = "YYYY-MM-DD";
	private final String TIME_FORMAT = "hh:mm";
	private final String MIDNIGHT = "00:00";
	private final String MINUTE_TO_MIDNIGHT = "23:59";

	/**
	 * SearchCommandHandler class<br/>
	 * This class contains all the logic for Search and display command, which
	 * will return the task queried by the user<br/>
	 */
	public SearchCommandHandler(IUserInterface ui, String originalCommand, HashMap<AttributeType, String> attrList) {
		super(ui, originalCommand, attrList);
	}

	/**
	 * Differentiate keyword display/search
	 * 
	 * @param cmdType
	 */
	public void execute(CommandType cmdType) {
		if (cmdType.equals(CommandType.display)) {
			displayTask();
		} else if (cmdType.equals(CommandType.search)) {
			searchTask();
		}
	}

	/**
	 * Display function <br/>
	 * Retrieve the parameter from -status <br/>
	 */
	private void displayTask() {
		TaskStatus status;

		try {
			status = TaskStatus.valueOf(_attrList.get(AttributeType.status));
		} catch (Exception e) {
			if (_attrList.get(AttributeType.status) != null && _attrList.get(AttributeType.status) != "") {
				writeToConsole("[Warning] Cannot recognize the status type. Display all instead\n");
			}
			status = TaskStatus.invalid;
		}

		displayTask(status);
	}

	/**
	 * Display function <br/>
	 * Only display the Task with this status <br/>
	 * 
	 * @param status
	 *            to filter
	 */
	private void displayTask(TaskStatus status) {

		if (status.equals(TaskStatus.free)) {
			Task task = new Task(_attrList.get(AttributeType.name), null, _attrList.get(AttributeType.description), null, null, status);
			getHistory().pushCmd(new Command(_originalCommand, CommandType.display, task));
			findFreeSlot();
		} else {
			List<Task> displayList = getStorage().displayTask(status);

			if (displayList.size() > 0) {
				String statusInString = "";
				if (status != TaskStatus.invalid) {
					statusInString = status.toString();
				}
				Task task = new Task(_attrList.get(AttributeType.name), null, _attrList.get(AttributeType.description), null, null, status);
				getHistory().pushCmd(new Command(_originalCommand, CommandType.display, task));
				passResultToConsole(displayList, String.format(DISPLAY_MESSAGE, statusInString));
			} else {
				writeToConsole(NO_RESULT_MESSAGE);
			}
		}
	}

	private void findFreeSlot() {
		if (checkFreeSlot()) {
			pushToHistoryAsNonExecutableCommand();
			String rangeString = _attrList.get(AttributeType.range);

			String[] temp = rangeString.trim().split(REGEX_SPLITTER);

			DateHelper dh = new DateHelper();
			int sum = dh.determineKeyword(temp);

			System.out.println(sum);
			if (sum < 0) {
				writeToConsole(RANGE_BELOW_ZERO);
			} else {
				DateTime startDate = getDateTime(_attrList.get(AttributeType.startingTime));
				DateTime endDate = getDateTime(_attrList.get(AttributeType.endingTime));

				if (startDate != null && endDate != null) {
					List<Task> freeSlots = getStorage().findFreeSlot(sum, startDate, endDate);
					writeToConsole(FREE_SLOTS);
					passFreeSlotsToConsole(freeSlots, "");
				} else {
					writeToConsole(INPUT_ERROR);
				}
			}
		}
	}

	/**
	 * Check for the syntax correctness of the find free slot <br/>
	 * 
	 * @return true if the syntax are correct, otherwise false (and the syntax
	 *         message will be displayed)
	 */
	private boolean checkFreeSlot() {
		if (_attrList.get(AttributeType.range) == null) {
			pushToHistoryAsNonExecutableCommand();
			writeToConsole(RANGE_NULL);
			return false;
		}
		if (_attrList.get(AttributeType.startingTime) == null) {
			pushToHistoryAsNonExecutableCommand();
			writeToConsole(STARTING_NULL);
			return false;
		}
		if (_attrList.get(AttributeType.endingTime) == null) {
			pushToHistoryAsNonExecutableCommand();
			writeToConsole(ENDING_NULL);
			return false;
		}

		return true;
	}

	/**
	 * Activate when command keyword is search <br/>
	 * Search tasks, supports name, description, startingTime and endingTime<br/>
	 * Display the search result if results are found. Display
	 * "No task match your search" if searchList size is 0
	 * 
	 */
	private void searchTask() {

		try {
			String name = _attrList.get(AttributeType.name);
			List<Task> searchList = null;
			List<Task> endingTimeList = null;
			List<Task> startingTimeList = null;
			List<Task> descriptionList = null;

			// Search for name
			if (name != null && !name.equals("")) {
				searchList = simpleSearch(AttributeType.name);
			}

			// Search for description
			if (_attrList.get(AttributeType.description) != null) {
				descriptionList = simpleSearch(AttributeType.description);
				if (searchList == null) {
					searchList = descriptionList;
				} else {
					searchList.removeAll(descriptionList);
					searchList.addAll(descriptionList);
				}
			}

			// Search for endingTime/due/to
			if (_attrList.get(AttributeType.endingTime) != null) {
				endingTimeList = dateTimeSearch(_attrList.get(AttributeType.endingTime), true);
				if (searchList == null) {
					searchList = endingTimeList;
				} else {
					searchList.removeAll(endingTimeList);
					searchList.addAll(endingTimeList);
				}
			}

			// Search for startingTime/from
			if (_attrList.get(AttributeType.startingTime) != null) {
				startingTimeList = dateTimeSearch(_attrList.get(AttributeType.startingTime), false);
				if (searchList == null) {

					searchList = startingTimeList;
				} else {
					searchList.removeAll(startingTimeList);
					searchList.addAll(startingTimeList);
				}
			}
			Task task = new Task(_attrList.get(AttributeType.name), null, _attrList.get(AttributeType.description), null, null, null);
			getHistory().pushCmd(new Command(_originalCommand, CommandType.search, task));

			if (searchList.size() == 0) {
				writeToConsole(NO_RESULT_MESSAGE);
			} else {
				passResultToConsole(searchList, SEARCH_MESSAGE);
			}

		} catch (NullPointerException e) {
			writeToConsole(NO_RESULT_MESSAGE);
		}
	}

	/**
	 * Search for only name
	 * 
	 * @return List of task
	 */
	private List<Task> simpleSearch(AttributeType at) {
		String[] varTemp = (_attrList.get(at)).split(REGEX_SPLITTER);
		return getStorage().searchTask(true, varTemp);
	}

	/**
	 * Search for date and time
	 * 
	 * @param textTime
	 * @return List of tasks
	 */
	private List<Task> dateTimeSearch(String textTime, boolean isEnd) {
		DateTime dt = getDateTime(textTime);
		List<Task> dateTimeList = null;

		AttributeType at = AttributeType.startingTime;
		String tempTime = _attrList.get(AttributeType.startingTime);
		if (isEnd) {
			at = AttributeType.endingTime;
			tempTime = _attrList.get(AttributeType.endingTime);
		}
		if (dt == null) {
			dateTimeList = getStorage().searchTask(at, true, tempTime);
		} else {
			System.out.println("asdasd" + dt.toString());

			if (dt.format(TIME_FORMAT).equals(MIDNIGHT) || dt.format(TIME_FORMAT).equals(MINUTE_TO_MIDNIGHT)) {
				dateTimeList = getStorage().searchTask(at, true, dt.format(DATE_FORMAT));
			} else {
				dateTimeList = getStorage().searchTask(at, true, dt.toString());
			}
		}

		return dateTimeList;
	}
}